package com.scs.application.modules.sys.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.scs.application.core.consts.Const;
import com.scs.application.core.exception.BusinessException;
import com.scs.application.core.query.QueryContext;
import com.scs.application.core.service.BaseServiceImpl;
import com.scs.application.core.service.IBaseFileService;
import com.scs.application.core.utils.CacheUtils;
import com.scs.application.core.utils.FileUtils;
import com.scs.application.core.utils.StringUtils;
import com.scs.application.modules.sys.dto.FlowDTO;
import com.scs.application.modules.sys.entity.Flow;
import com.scs.application.modules.sys.mapper.FlowMapper;
import com.scs.application.modules.sys.service.FlowService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 */
@Service
public class FileFlowServiceImpl extends BaseServiceImpl<FlowMapper, Flow> implements FlowService, IBaseFileService<Flow, FlowDTO> {

    @Override
    public Flow findByFlowKey(String flowKey) {
        FlowDTO flowDTO = getFlowDTOByCode(flowKey);
        Flow flow = new Flow();
        BeanUtils.copyProperties(flowDTO, flow);
        return flow;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Flow copy(String flowKey) {
        FlowDTO flowDTO = getFlowDTOByCode(flowKey);
        flowDTO.setCode(flowKey + "-copy")
                .setName(flowDTO.getName() + "-副本")
                .setRemark(flowKey + "流程副本")
                .setId(StringUtils.getIdStr());

        //必须赋新的ID，否则会导致删除时删除两个
        if (flowDTO.getFlowButtons() != null) {
            flowDTO.getFlowButtons().forEach(flowButton -> {
                flowButton.setId(StringUtils.getIdStr()).setFlowId(flowDTO.getId());
            });
        }

        if (flowDTO.getFlowNodes() != null) {
            flowDTO.getFlowNodes().forEach(flowNode -> {
                flowNode.setId(StringUtils.getIdStr()).setFlowId(flowDTO.getId());
            });
        }

        String fileNameWithPath = Const.DATA_VIEW_FLOW_SRC + flowDTO.getCode() + ".json";
        String result = JSON.toJSONString(flowDTO, SerializerFeature.PrettyFormat);
        FileUtils.writeTxtFile(fileNameWithPath, result);

        Flow copyFlow = new Flow();
        BeanUtils.copyProperties(flowDTO, copyFlow);
        return copyFlow;
    }

    @Override
    public boolean removeCascadeById(Serializable id) {
        List<Flow> flows = list();
        for (Flow flow : flows) {
            if (id.equals(flow.getId())) {
                FileUtils.deleteFile(Const.DATA_VIEW_FLOW_SRC + flow.getCode() + ".json");
                CacheUtils.remove(CacheUtils.FLOW_NODE_CACHE, flow.getCode());
                CacheUtils.remove(CacheUtils.FLOW_BUTTON_CACHE, flow.getCode());
                break;
            }
        }
        return true;
    }

    @Override
    public Flow getById(Serializable id) {
        Flow flow = null;
        List<Flow> flows = list();
        for (Flow flow1 : flows) {
            if (id.equals(flow1.getId())) {
                flow = flow1;
                break;
            }
        }
        return flow;
    }

    @Override
    public boolean removeCascadeByIds(Collection<? extends Serializable> idList) {
        List<Flow> flows = list();
        for (Flow flow : flows) {
            if (idList.contains(flow.getId())) {
                FileUtils.deleteFile(Const.DATA_VIEW_FLOW_SRC + flow.getCode() + ".json");
                CacheUtils.remove(CacheUtils.FLOW_NODE_CACHE, flow.getCode());
                CacheUtils.remove(CacheUtils.FLOW_BUTTON_CACHE, flow.getCode());
            }
        }
        return true;
    }

    @Override
    public Page<Flow> page(QueryContext context) {
        List<Flow> flows = list(context);
        return filePage(context, flows);
    }

    @Override
    public List<Flow> list() {
        QueryContext context = new QueryContext();
        return list(context);
    }

    @Override
    public List<Flow> list(QueryContext context) {
        List<FlowDTO> flowDTOS = getDTOList();
        List<Flow> flows = Lists.newArrayListWithCapacity(flowDTOS.size());
        for (FlowDTO flowDTO : flowDTOS) {
            Flow flow = new Flow();
            com.scs.application.core.utils.BeanUtils.copyProperties(flowDTO, flow);
            flows.add(flow);
        }
        if (context != null && context.getParams() != null) {
            String code = context.getParams().get("code") != null ? context.getParams().get("code").toString() : "";
            String name = context.getParams().get("name") != null ? context.getParams().get("name").toString() : "";
            flows = flows.stream().filter(flow -> (StringUtils.isNotBlank(code) ? flow.getCode().indexOf(code) > -1 : true)
                    && (StringUtils.isNotBlank(name) ? flow.getName().indexOf(name) > -1 : true)).collect(Collectors.toList());
        }
        return flows;
    }

    @Override
    public FlowDTO getFlowDTOByCode(String flowCode) {
        FlowDTO flowDTO = new FlowDTO();
        setFlowDTO(flowDTO, Const.getFlowSrc() + flowCode + ".json");
        if (StringUtils.isBlank(flowDTO.getCode()) || StringUtils.isBlank(flowDTO.getId())) {
            return null;
        }
        return flowDTO;
    }

    @Override
    public FlowDTO getFlowDTOById(String flowId) {
        List<FlowDTO> flowDTOS = getDTOList();
        Optional<FlowDTO> optFlowDTO = flowDTOS.stream().filter(flowDTO -> flowId.equalsIgnoreCase(flowDTO.getId())).findFirst();
        if (optFlowDTO.isPresent()) {
            return optFlowDTO.get();
        }
        return null;
    }

    @Override
    public List<FlowDTO> getDTOList() {
        return getDTOList(Const.getFlowSrc());
    }

    @Override
    public FlowDTO getDTO(String fileSrc) {
        FlowDTO flowDTO = new FlowDTO();
        setFlowDTO(flowDTO, fileSrc);
        return flowDTO;
    }

    @Override
    public <Flow> boolean saveBatch(Class<Flow> modelClass, Collection<Flow> entityList, int batchSize) {
        return false;
    }

    @Override
    public boolean saveOrUpdate(Flow entity) {
        List<Flow> flows = list();
        if (entity.getId() == null) {
            entity.setId(StringUtils.getIdStr());
        } else {
            flows.stream().filter(flow -> entity.getCode().equalsIgnoreCase(flow.getCode())
                    && !entity.getId().equalsIgnoreCase(flow.getId())).findAny().ifPresent(flow -> {
                throw new BusinessException("Key已经存在，请重新命名！");
            });
        }
        File path = new File(Const.DATA_VIEW_FLOW_SRC);
        if (!path.exists()) {
            path.mkdirs();
        }

        String fileNameWithPath = Const.DATA_VIEW_FLOW_SRC + entity.getCode() + ".json";

        FlowDTO flowDTO = getFlowDTOById(entity.getId());
        if (flowDTO == null) {
            flowDTO = new FlowDTO();
        } else if (!flowDTO.getCode().equalsIgnoreCase(entity.getCode())) { //如果CODE发生变化，删除旧的JSON数据
            FileUtils.deleteFile(Const.DATA_VIEW_FLOW_SRC + flowDTO.getCode() + ".json");
        }
        com.scs.application.core.utils.BeanUtils.copyProperties(entity, flowDTO);
        String result = JSON.toJSONString(flowDTO, SerializerFeature.PrettyFormat);
        FileUtils.writeTxtFile(fileNameWithPath, result);
        CacheUtils.remove(CacheUtils.FLOW_BUTTON_CACHE, flowDTO.getCode());
        CacheUtils.remove(CacheUtils.FLOW_NODE_CACHE, flowDTO.getCode());
        return true;
    }

    private void setFlowDTO(FlowDTO flowDTO, String fileNameWithPath) {
        String dataViewString = FileUtils.readFileStore(fileNameWithPath);
        if (StringUtils.isEmpty(dataViewString)) {
            return;
        }
        JSONObject jsonObject = JSONObject.parseObject(dataViewString);
        flowDTO.setId(jsonObject.getString("id"))
                .setCode(jsonObject.getString("code"))
                .setName(jsonObject.getString("name"))
                .setFieldName(jsonObject.getString("fieldName"))
                .setFlagDisable(jsonObject.getBoolean("flagDisable"))
                .setFlagCheckExecution(jsonObject.getBoolean("flagCheckExecution"))
                .setRemark(jsonObject.getString("remark"));

        flowDTO.setFlowNodes(Lists.newArrayList()).setFlowButtons(Lists.newArrayList());

        String strFlowButtons = jsonObject.getString("flowButtons");
        if (StringUtils.isNotBlank(strFlowButtons)) {
            JSONArray jsonArray = JSONArray.parseArray(strFlowButtons);
            if (jsonArray != null && !jsonArray.isEmpty()) {
                List<FlowDTO.FlowButtonDTO> flowButtonDTOS = Lists.newArrayListWithCapacity(jsonArray.size());
                jsonArray.forEach(obj -> {
                    JSONObject jsonObj = JSONObject.parseObject(obj.toString());
                    FlowDTO.FlowButtonDTO flowButtonDTO = new FlowDTO.FlowButtonDTO();
                    flowButtonDTO.setId(jsonObj.getString("id"))
                            .setCode(jsonObj.getString("code"))
                            .setFlagBatch(jsonObj.getBoolean("flagBatch"))
                            .setFlagClose(jsonObj.getBoolean("flagClose"))
                            .setFlagComment(jsonObj.getBoolean("flagComment"))
                            .setFlagEnd(jsonObj.getBoolean("flagEnd"))
                            .setFlagStart(jsonObj.getBoolean("flagStart"))
                            .setFlowId(jsonObj.getString("flowId"))
                            .setIcon(jsonObj.getString("icon"))
                            .setRemark(jsonObj.getString("remark"))
                            .setSort(jsonObj.getInteger("sort"))
                            .setType(jsonObj.getString("type"))
                            .setValueIf(jsonObj.getString("valueIf"))
                            .setValueIfTitle(jsonObj.getString("valueIfTitle"))
                            .setTitle(jsonObj.getString("title"))
                            .setValueTo(jsonObj.getString("valueTo"))
                            .setRoleCodes(jsonObj.getJSONArray("roleCodes"))
                            .setUserCodes(jsonObj.getJSONArray("userCodes"))
                            .setFlagConfirm(jsonObj.getBoolean("flagConfirm"))
                            .setConfirmMsg(jsonObj.getString("confirmMsg"))
                            .setFlagBatch2(jsonObj.getBoolean("flagBatch2"))
                    ;
                    flowButtonDTOS.add(flowButtonDTO);
                });
                flowDTO.setFlowButtons(flowButtonDTOS);
            }
        }

        String strFlowNodes = jsonObject.getString("flowNodes");
        if (StringUtils.isNotBlank(strFlowNodes)) {
            JSONArray jsonArray = JSONArray.parseArray(strFlowNodes);
            if (jsonArray != null && !jsonArray.isEmpty()) {
                List<FlowDTO.FlowNodeDTO> flowNodeDTOS = Lists.newArrayListWithCapacity(jsonArray.size());
                jsonArray.forEach(obj -> {
                    JSONObject jsonObj = JSONObject.parseObject(obj.toString());
                    FlowDTO.FlowNodeDTO flowNodeDTO = new FlowDTO.FlowNodeDTO();
                    flowNodeDTO.setId(jsonObj.getString("id"))
                            .setColor(jsonObj.getString("color"))
                            .setFlagShowVbAdd(jsonObj.getBoolean("flagShowVbAdd"))
                            .setFlagShowVbDelete(jsonObj.getBoolean("flagShowVbDelete"))
                            .setFlagShowVbRefresh(jsonObj.getBoolean("flagShowVbRefresh"))
                            .setFlagShowVfbAdd(jsonObj.getBoolean("flagShowVfbAdd"))
                            .setFlagShowVfbDelete(jsonObj.getBoolean("flagShowVfbDelete"))
                            .setFlagShowVfbSave(jsonObj.getBoolean("flagShowVfbSave"))
                            .setFlagShowVrbDelete(jsonObj.getBoolean("flagShowVrbDelete"))
                            .setFlagShowVrbEdit(jsonObj.getBoolean("flagShowVrbEdit"))
                            .setFlagShowVrbView(jsonObj.getBoolean("flagShowVrbView"))
                            .setFlowId(jsonObj.getString("flowId"))
                            .setName(jsonObj.getString("name"))
                            .setRoleCodes(jsonObj.getJSONArray("roleCodes"))
                            .setSort(jsonObj.getInteger("sort"))
                            .setValue(jsonObj.getString("value"));
                    flowNodeDTOS.add(flowNodeDTO);
                });
                flowDTO.setFlowNodes(flowNodeDTOS);
            }
        }
    }

    @Override
    public Boolean isSupport(Boolean flagProject) {
        return !flagProject;
    }

}
